package LecteurMp3;
import javax.sound.sampled.*;

import sheepmixView.client.model.Musique;


import com.mpatric.mp3agic.ID3v1;
import com.mpatric.mp3agic.ID3v2;
import com.mpatric.mp3agic.Mp3File;

import java.io.*;


public class Mp3Player {
	private File file;
	private AudioInputStream audioInputStream;
	private SourceDataLine sourceDataLine;
	private Mixer mixer;
	private PlayerThread playerThread;
	private AudioFormat audioFormat;
	private String path;
	private ID3v1 id3v1 = null;
	private ID3v2 id3v2 = null;
	private Mp3File mp3;
	private final Timer timer;
	private final float START_VOL = -40;
	private boolean isPlaying = false;
	private int duration;


	public Mp3Player(Musique playedMp3, Mixer mix) throws Exception
	{

		try {
			this.path = playedMp3.getChemin();
			mixer = mix;
			// Insert ID3 Tags
			mp3 = new Mp3File(path);
			duration = (int)mp3.getLengthInMilliseconds() / 1000;

			// Creation du tag
			if (mp3.hasId3v2Tag()) {
				id3v2 = mp3.getId3v2Tag();
			}
			else if (mp3.hasId3v1Tag()) {
				id3v1 = mp3.getId3v1Tag();
			}
			file = new File(path);
			System.out.println(file.toString());
			AudioInputStream baseInputStream = AudioSystem.getAudioInputStream(file);

			AudioFormat baseFormat = baseInputStream.getFormat();


			audioFormat = new AudioFormat(
					AudioFormat.Encoding.PCM_SIGNED,  // Encoding to use
					baseFormat.getSampleRate(),	  // sample rate (same as base format)
					16,				                  // sample size in bits (thx to Javazoom)
					baseFormat.getChannels(),	      // # of Channels
					baseFormat.getChannels()*2,	  // Frame Size
					baseFormat.getSampleRate(),	  // Frame Rate
					false				              // Big Endian
					);

			audioInputStream = AudioSystem.getAudioInputStream(audioFormat, baseInputStream);
			DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
			sourceDataLine = (SourceDataLine)mixer.getLine(lineInfo);
		}	
		catch (Exception e) {
			System.out.println(e);
			throw new Exception("InvalidFormatException");
		}


		timer = new Timer(1000,(int)getDuration()-Glb.getFadeDuration());
		playerThread = new PlayerThread();

	}

	public int getDuration() {
		return duration;
	}

	public void fadeOut(int sec) {

		int i = 0;
		int x = sec*50;

		while (i < sec*10) {

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			i++;

			if(((float)(getCurrentVolume()- Math.sqrt(1.5*Math.abs(getMinVolume()/(x)))) > -80))
				setVolume((float)(getCurrentVolume()- Math.sqrt(1.5*Math.abs(getMinVolume()/(x)))));

		}	
		stop();

	}

	public boolean isPlaying() {
		return isPlaying;
	}

	public Timer getTimer() {
		return timer;
	}

	public AudioFormat getFileFormat() {
		return audioFormat;
	}

	public void play(int sec)
	{
		isPlaying = true;
		playerThread.start();
		int i = 0;
		int x = sec*50;

		while (i < sec*10) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			i++;
			if((float)(getCurrentVolume() + Math.sqrt(1.5*Math.abs(getMinVolume()/(x)))) < 1)
				setVolume((float)(getCurrentVolume() + Math.sqrt(1.5*Math.abs(getMinVolume()/(x)))));
		}
		System.out.println("Current Volume : " + getCurrentVolume());
	}


	public synchronized void stop()
	{
		if(playerThread.isAlive())
		{

			playerThread.stopPlayback();
			if(timer.isAlive()) {
				timer.reset();
				timer.stop();
			}

		}
		isPlaying = false;
	}

	public void pause()
	{
		//TODO PAUSE IN SOUNDFILE
	}

	public void rewind()
	{
		try {
			audioInputStream = AudioSystem.getAudioInputStream(file);
		} catch (UnsupportedAudioFileException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		audioInputStream = AudioSystem.getAudioInputStream(audioFormat, audioInputStream);
	}

	public void close()
	{
		try {
			audioInputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String getFilename() {
		return path;
	}

	public Mixer getMixer() {
		return mixer;
	}

	public int getMediaPosition() {
		return  timer.getTotalSecondes();
	}

	public byte[] getSoundData() {
		rewind();
		long frameLenght = audioInputStream.getFrameLength();
		int frameSize = audioFormat.getFrameSize();
		int soundSize = (int)frameLenght * frameSize;
		byte [] soundData = new byte[soundSize];
		try {
			audioInputStream.read(soundData, 0, soundSize);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return soundData;
	}

	public void setVolume(float vlm) {

		if(sourceDataLine.isActive())
		{
			FloatControl c;
			if(sourceDataLine.isControlSupported(FloatControl.Type.MASTER_GAIN))
			{
				c = (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
				c.setValue(vlm);
			}
		}


	}
	
	public void upVolume() {
		if (getCurrentVolume() + .5 < 6.0)
			setVolume((float) (getCurrentVolume() + .5));
	}
	
	public void downVolume() {
		if (getCurrentVolume() - .5 > -40)
			setVolume((float) (getCurrentVolume() - .5));
	}

	public void drain() {
		if(sourceDataLine.isActive())
		{
			//System.out.println("Draining");
			sourceDataLine.drain();
		}
		else
			System.out.println("Not Draining because it's not active");		
	}

	public float getMaxVolume() {
		FloatControl c;
		if(sourceDataLine.isControlSupported(FloatControl.Type.MASTER_GAIN))
		{
			c = (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
			return c.getMaximum();
		}
		else
			return 0;
	}

	public float getMinVolume() {
		FloatControl c;
		if(sourceDataLine.isControlSupported(FloatControl.Type.MASTER_GAIN))
		{
			c = (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
			return c.getMinimum();
		}
		else
			return 0;
	}

	public float getCurrentVolume() {
		try {
			FloatControl c;
			if(sourceDataLine.isControlSupported(FloatControl.Type.MASTER_GAIN))
			{
				c = (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
				return c.getValue();
			}
			else
				return 0;
		}
		catch(Exception e) {
			System.out.println("Exception getCurrentVolume");
			return 0;
		}
	}

	public String obtenirTitre() {
		if (id3v2 != null)
			return id3v2.getTitle();
		else if (id3v1 != null)
			return id3v1.getTitle();
		else
			return "Inconnu";
	}

	public String obtenirAuteur() {
		if (id3v2 != null)
			return id3v2.getArtist();
		else if (id3v1 != null)
			return id3v1.getArtist();
		else
			return "Inconnu";
	}

	public String obtenirGenre() {
		if (id3v2 != null)
			return id3v2.getGenreDescription();
		else if (id3v1 != null)
			return id3v1.getGenreDescription();
		else
			return "Inconnu";
	}

	public String obtenirAlbum() {
		if (id3v2 != null)
			return id3v2.getAlbum();
		else if (id3v1 != null)
			return id3v1.getAlbum();
		else
			return "Inconnu";
	}

	//---------------------Internal class
	class PlayerThread extends Thread
	{
		private byte [] tempBuffer;
		private boolean stop = false;

		public PlayerThread() throws Exception
		{
			int myBufferSize;
			try {
				myBufferSize = audioFormat.getFrameSize()*10000;
			}
			catch(Exception e) {
				myBufferSize = 40000;
				//myBufferSize = 200;
			}
			tempBuffer = new byte[myBufferSize];

		}

		public void stopPlayback()
		{
			//System.out.println("stopping");
			stop = true;
		}

		@Override
		public void run()
		{
			int cnt = 0;
			stop = false;

			try
			{
				sourceDataLine.open(audioFormat);
			}catch(LineUnavailableException e)
			{
				e.printStackTrace();
			}
			sourceDataLine.start();
			try
			{
				int bufferSpace;
				timer.demarrer();


				bufferSpace = sourceDataLine.available();
				if(bufferSpace > tempBuffer.length)
				{
					cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length);
					FloatControl c = (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
					c.setValue(START_VOL);
					if(cnt != -1)
					{
						sourceDataLine.write(tempBuffer, 0, cnt);
					}	

				}

				while(cnt != -1 && !stop)
				{
					bufferSpace = sourceDataLine.available();
					if(bufferSpace > tempBuffer.length)
					{
						cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length);
						if(cnt != -1)
						{
							sourceDataLine.write(tempBuffer, 0, cnt);
						}	
					}
					Thread.sleep(1);
				}
			}catch(Exception e)
			{
				e.printStackTrace();
			}

			try {
				if(!stop)
				{
					sourceDataLine.drain();
				}
				sourceDataLine.flush();
				sourceDataLine.close();
				audioInputStream.close();

				timer.stop();
				timer.reset();
				rewind();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}
